package learning.maxent.training;

import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import learning.data.Dataset;
import learning.data.document.InstanceDocument;
import learning.maxent.training.Maxent.DecodingResult;
import learning.util.SparseVector;

public class FormatHelper {

	public static String formatDocument(InstanceDocument doc) {
        StringBuilder sb = new StringBuilder();
        sb.append(doc.label + "\t" + doc.token);
        return (sb.toString());
	}
	
	// print an example document with its predicted and expected tag sequences
	public static String formatExample(InstanceDocument doc, DecodingResult predictedParse, DecodingResult trueParse) {
		
        StringBuilder sb = new StringBuilder();
        sb.append(trueParse.label + "\t" + predictedParse.label + "\t" + doc.token);
        sb.append("\n");
        return (sb.toString());
	}
	
	// print the features with highest weights for each state transition
	public static String formatParams(MaxentParameters p, Dataset<InstanceDocument> dataset) {
		NumberFormat format = new DecimalFormat("0.00");

		String[] names = new String[p.model.numStates];
		List[] lists = new List[p.model.numStates];
		int[] widths = new int[p.model.numStates];
		
		for (int i=0; i < p.parameters.length; i++) {
			names[i] = i + "";
			SparseVector v = p.parameters[i];
			List<Pair> pairs = new ArrayList<Pair>(v.num);
				
			for (int k=0; k < v.num; k++) pairs.add 
				(new Pair(v.ids[k], dataset.getFeatureName(v.ids[k]), v.vals[k]));
				
			Collections.sort(pairs);
			int w = 1;
			for (int k=0; k < Math.min(10, pairs.size()); k++)
				w = Math.max(w, pairs.get(k).feature.length()+1);
			widths[i] = w + 6;
			lists[i] = pairs;			
		}

		StringBuilder sb = new StringBuilder();
		
		for (int i=0; i < names.length; i++) {			
			sb.append(String.format("%1$-" + (widths[i]) + "s", names[i]));
		}
		sb.append("\n");
		for (int l=0; l < 10; l++) {
			for (int i=0; i < lists.length; i++) {
				if (lists[i].size() > l) {
					Pair pa = (Pair)lists[i].get(l);
					
					sb.append(String.format("%1$-" + (widths[i]) + "s",
							format.format(pa.score) + " " + pa.feature));					
				}				
			}
			sb.append("\n");
		}
		
		return (sb.toString());
	}
	
	// print the features with highest weights
	public static String formatVector(SparseVector v, Dataset<InstanceDocument> dataset) {
		NumberFormat format = new DecimalFormat("0.00");
		
		List<Pair> pairs = new ArrayList<Pair>(v.num);
		
		for (int k=0; k < v.num; k++) pairs.add 
			(new Pair(v.ids[k], dataset.getFeatureName(v.ids[k]), v.vals[k]));
		
		Collections.sort(pairs);

		StringBuilder sb = new StringBuilder();
		for (int i=0; i < pairs.size(); i++) {
			Pair pa = (Pair)pairs.get(i);
			
			sb.append(format.format(pa.score) + " " + pa.feature + "\n");					
		}
		return (sb.toString());
	}
	
	private static class Pair implements Comparable<Pair> {
		int id;
		String feature;
		float score;
		Pair(int id, String feature, float score) { 
			this.id = id; this.feature = feature; this.score = score; 
		}
		
		public int compareTo(Pair p) {
			if (Math.abs(score) < Math.abs(p.score)) return 1; 
			else if (Math.abs(score) > Math.abs(p.score)) return -1; 
			return 0;
		}
	}

	// print confusion matrix
	public static String formatConfusionMatrix(Evaluator evaluator) {
		DecimalFormat format = new DecimalFormat("0.00000");
		String ansiRed    = "\u0027[31m";
		String ansiNormal = "\u0027[0m";
		boolean useAnsi = false;
		
		int numLabels = evaluator.getNumLabels();
		int[][] confusionMatrix = evaluator.getConfusionMatrix();

		int[] trueCounts = new int[numLabels]; 
		for (int i=0; i < numLabels; i++)
			for (int j=0; j < numLabels; j++)
				trueCounts[i] += confusionMatrix[i][j];
		
		StringBuilder sb = new StringBuilder();
		for (int i=0; i < numLabels; i++) {
			sb.append("\t" + i);
		}
		sb.append("\n");
		
		for (int i=0; i < numLabels; i++) {
			sb.append(i);
			
			for (int j=0; j < numLabels; j++) {
				sb.append("\t");
				float f = (float)(confusionMatrix[i][j] / (double)trueCounts[i]);
				String n = format.format(f);
				if (f > 0 && useAnsi) n = ansiRed + n + ansiNormal;
				sb.append(n);
			}
			sb.append("\n");
		}
		return sb.toString();
	}
}
